/***************************************************************************
 *                                                                         *
 * Copyright (c) 2008 Nuvoton Technolog. All rights reserved.              *
 *                                                                         *
 ***************************************************************************/
 
/****************************************************************************
* FILENAME
*   wb_aic.c
*
* VERSION
*   1.0
*
* DESCRIPTION
*   The AIC related functions of Nuvoton ARM9 MCU
*
* HISTORY
*   2008-06-25  Ver 1.0 draft by Min-Nan Cheng
*
* REMARK
*   None
 **************************************************************************/
#include <stdio.h>
#include "wblib.h"

#define WB_MIN_INT_SOURCE  1
#define WB_MAX_INT_SOURCE  31
#define WB_NUM_OF_AICREG   32

/* Global variables */
BOOL volatile _sys_bIsAICInitial = FALSE;
BOOL volatile _sys_bIsHWMode = TRUE;

/* declaration the function prototype */
extern VOID WB_Interrupt_Shell(VOID);

/* Define AIC source control register address */
UINT32 WB_AICRegAddr[WB_NUM_OF_AICREG] = { 0,
				REG_AIC_SCR1,	REG_AIC_SCR2,	REG_AIC_SCR3,	REG_AIC_SCR4,
				REG_AIC_SCR5,	REG_AIC_SCR6,	REG_AIC_SCR7,	REG_AIC_SCR8,								
				REG_AIC_SCR9,	REG_AIC_SCR10,	REG_AIC_SCR11,	REG_AIC_SCR12,
				REG_AIC_SCR13,	REG_AIC_SCR14,	REG_AIC_SCR15,	REG_AIC_SCR16,
				REG_AIC_SCR17,	REG_AIC_SCR18,	REG_AIC_SCR19,	REG_AIC_SCR20,
				REG_AIC_SCR21,	REG_AIC_SCR22,	REG_AIC_SCR23,	REG_AIC_SCR24,
				REG_AIC_SCR25,	REG_AIC_SCR26,  REG_AIC_SCR27,  REG_AIC_SCR28,
				REG_AIC_SCR29,  REG_AIC_SCR30,  REG_AIC_SCR31
				};

/* Interrupt Handler Table */
typedef void (*sys_pvFunPtr)();   /* function pointer */
sys_pvFunPtr sysIrqHandlerTable[] = { 0,                /* 0 */
                                  WB_Interrupt_Shell,	/* 1 */
                                  WB_Interrupt_Shell,	/* 2 */
                                  WB_Interrupt_Shell,	/* 3 */
                                  WB_Interrupt_Shell,	/* 4 */
                                  WB_Interrupt_Shell,	/* 5 */
                                  WB_Interrupt_Shell,	/* 6 */
                                  WB_Interrupt_Shell,	/* 7 */
                                  WB_Interrupt_Shell,	/* 8 */
                                  WB_Interrupt_Shell,	/* 9 */
                                  WB_Interrupt_Shell,	/* 10 */
                                  WB_Interrupt_Shell,	/* 11 */
                                  WB_Interrupt_Shell,	/* 12 */
                                  WB_Interrupt_Shell,	/* 13 */
                                  WB_Interrupt_Shell,	/* 14 */
                                  WB_Interrupt_Shell,	/* 15 */
                                  WB_Interrupt_Shell,	/* 16 */
                                  WB_Interrupt_Shell,	/* 17 */
                                  WB_Interrupt_Shell,	/* 18 */
                                  WB_Interrupt_Shell,	/* 19 */
                                  WB_Interrupt_Shell,	/* 20 */
                                  WB_Interrupt_Shell,	/* 21 */
                                  WB_Interrupt_Shell,	/* 22 */
                                  WB_Interrupt_Shell,	/* 23 */
                                  WB_Interrupt_Shell,	/* 24 */
                                  WB_Interrupt_Shell,	/* 25 */
                                  WB_Interrupt_Shell,	/* 26 */
                                  WB_Interrupt_Shell,	/* 27 */
                                  WB_Interrupt_Shell,	/* 28 */
                                  WB_Interrupt_Shell,	/* 29 */
                                  WB_Interrupt_Shell,	/* 30 */
                                  WB_Interrupt_Shell	/* 31 */
                                };

sys_pvFunPtr sysFiqHandlerTable[] = { 0,
                                  WB_Interrupt_Shell,	/* 1 */
                                  WB_Interrupt_Shell,	/* 2 */
                                  WB_Interrupt_Shell,	/* 3 */
                                  WB_Interrupt_Shell,	/* 4 */
                                  WB_Interrupt_Shell,	/* 5 */
                                  WB_Interrupt_Shell,	/* 6 */
                                  WB_Interrupt_Shell,	/* 7 */
                                  WB_Interrupt_Shell,	/* 8 */
                                  WB_Interrupt_Shell,	/* 9 */
                                  WB_Interrupt_Shell,	/* 10 */
                                  WB_Interrupt_Shell,	/* 11 */
                                  WB_Interrupt_Shell,	/* 12 */
                                  WB_Interrupt_Shell,	/* 13 */
                                  WB_Interrupt_Shell,	/* 14 */
                                  WB_Interrupt_Shell,	/* 15 */
                                  WB_Interrupt_Shell,	/* 16 */
                                  WB_Interrupt_Shell,	/* 17 */
                                  WB_Interrupt_Shell,	/* 18 */
                                  WB_Interrupt_Shell,	/* 19 */
                                  WB_Interrupt_Shell,	/* 20 */
                                  WB_Interrupt_Shell,	/* 21 */
                                  WB_Interrupt_Shell,	/* 22 */
                                  WB_Interrupt_Shell,	/* 23 */
                                  WB_Interrupt_Shell,	/* 24 */
                                  WB_Interrupt_Shell,	/* 25 */
                                  WB_Interrupt_Shell,	/* 26 */
                                  WB_Interrupt_Shell,	/* 27 */
                                  WB_Interrupt_Shell,	/* 28 */
                                  WB_Interrupt_Shell,	/* 29 */
                                  WB_Interrupt_Shell,	/* 30 */
                                  WB_Interrupt_Shell	/* 31 */
                                };

/* Interrupt Handler */
__irq VOID sysIrqHandler()
{
	if (_sys_bIsHWMode)
	{
		UINT32 volatile _mIPER, _mISNR;

		_mIPER = inpw(REG_AIC_IPER) >> 2;
		_mISNR = inpw(REG_AIC_ISNR);
		if (_mISNR != 0)
			if (_mIPER == _mISNR)
				(*sysIrqHandlerTable[_mISNR])();
		outpw(REG_AIC_EOSCR, 1);
	}
	else
	{
		UINT32 volatile _mISR, i;

		_mISR = inpw(REG_AIC_ISR);
		for (i=1; i<WB_NUM_OF_AICREG; i++)
			if (_mISR & (1 << i))
				(*sysIrqHandlerTable[i])();
	}
}

__irq VOID sysFiqHandler()
{
	if (_sys_bIsHWMode)
	{
		UINT32 volatile _mIPER, _mISNR;
  
		_mIPER = inpw(REG_AIC_IPER) >> 2;
		_mISNR = inpw(REG_AIC_ISNR);
		if (_mISNR != 0)
			if (_mIPER == _mISNR)
				(*sysFiqHandlerTable[_mISNR])();
		outpw(REG_AIC_EOSCR, 1);
	}
	else
	{
		UINT32 volatile _mISR, i;

		_mISR = inpw(REG_AIC_ISR);
		for (i=1; i<WB_NUM_OF_AICREG; i++)
			if (_mISR & (1 << i))
				(*sysFiqHandlerTable[i])();
	}
}

VOID WB_Interrupt_Shell()
{
}

VOID sysInitializeAIC()
{
   PVOID _mOldIrqVect, _mOldFiqVect;

   _mOldIrqVect = *(PVOID volatile *)0x38;
   *(PVOID volatile *)0x38 = (PVOID volatile)sysIrqHandler;

   _mOldFiqVect = *(PVOID volatile *)0x3C;
   *(PVOID volatile *)0x3C = (PVOID volatile)sysFiqHandler;
}


/* Interrupt library functions */
INT32 sysDisableInterrupt(INT_SOURCE_E eIntNo)
{
   if ((eIntNo > WB_MAX_INT_SOURCE) || (eIntNo < WB_MIN_INT_SOURCE))
      return Fail;

   outpw(REG_AIC_MDCR, (1 << eIntNo));
   return Successful;
}

INT32 sysDisableGroupInterrupt(INT_SOURCE_GROUP_E eIntNo)
{
   outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) & ~eIntNo);
   return Successful;
}

INT32 sysEnableInterrupt(INT_SOURCE_E eIntNo)
{
   if ((eIntNo > WB_MAX_INT_SOURCE) || (eIntNo < WB_MIN_INT_SOURCE))
      return Fail;

   outpw(REG_AIC_MECR, (1 << eIntNo));
   return Successful;
}

INT32 sysEnableGroupInterrupt(INT_SOURCE_GROUP_E eIntNo)
{
   outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) | eIntNo);
   return Successful;
}

UINT32 sysGetGroupInterruptStatus()
{
	UINT32 status;

	status = inpw(REG_AIC_GASR);
	return status;
}

PVOID sysInstallExceptionHandler(INT32 nExceptType, PVOID pvNewHandler)
{
   PVOID _mOldVect;
   
   switch (nExceptType)
   {
      case WB_SWI:
           _mOldVect = *(PVOID volatile *)0x28;
           *(PVOID volatile *)0x28 = pvNewHandler;
           break;

      case WB_D_ABORT:
           _mOldVect = *(PVOID volatile *)0x30;
           *(PVOID volatile *)0x30 = pvNewHandler;
           break;

      case WB_I_ABORT:
           _mOldVect = *(PVOID volatile *)0x2C;
           *(PVOID volatile *)0x2C = pvNewHandler;
           break;

      case WB_UNDEFINE:
           _mOldVect = *(PVOID volatile *)0x24;
           *(PVOID volatile *)0x24 = pvNewHandler;
           break;

      default:
           ;
   }
   return _mOldVect;
}

PVOID sysInstallFiqHandler(PVOID pvNewISR)
{
   PVOID _mOldVect;
   
   _mOldVect = *(PVOID volatile *)0x3C;
   *(PVOID volatile *)0x3C = pvNewISR;
   return _mOldVect;
}

PVOID sysInstallIrqHandler(PVOID pvNewISR)
{
   PVOID _mOldVect;
   
   _mOldVect = *(PVOID volatile *)0x38;
   *(PVOID volatile *)0x38 = pvNewISR;
   return _mOldVect;
}


PVOID sysInstallISR(INT32 nIntTypeLevel, INT_SOURCE_E eIntNo, PVOID pvNewISR)
{
   PVOID  _mOldVect;
   UINT32 _mRegAddr, _mRegValue;

   if (!_sys_bIsAICInitial)
   {
      sysInitializeAIC();
      _sys_bIsAICInitial = TRUE;
   }
   
   _mRegAddr = WB_AICRegAddr[eIntNo];
   _mRegValue = inpw(_mRegAddr) & 0xFFFFFFF8;
   outpw(_mRegAddr, (_mRegValue | nIntTypeLevel));

   switch (nIntTypeLevel)
   {
      case FIQ_LEVEL_0:
           _mOldVect = (PVOID) sysFiqHandlerTable[eIntNo];
           sysFiqHandlerTable[eIntNo] = (sys_pvFunPtr)pvNewISR;
           break;
   
      case IRQ_LEVEL_1:
      case IRQ_LEVEL_2:
      case IRQ_LEVEL_3:
      case IRQ_LEVEL_4:
      case IRQ_LEVEL_5:
      case IRQ_LEVEL_6:
      case IRQ_LEVEL_7:
           _mOldVect = (PVOID) sysIrqHandlerTable[eIntNo];
           sysIrqHandlerTable[eIntNo] = (sys_pvFunPtr)pvNewISR;
           break;

      default:
           ;
   }
   return _mOldVect;
}

INT32 sysSetGlobalInterrupt(INT32 nIntState)
{
   switch (nIntState)
   {
      case ENABLE_ALL_INTERRUPTS:
           outpw(REG_AIC_MECR, 0xFFFFFFFF);
           break;

      case DISABLE_ALL_INTERRUPTS:
           outpw(REG_AIC_MDCR, 0xFFFFFFFF);
           break;

      default:
           ;
   }
   return Successful;
}

INT32 sysSetInterruptPriorityLevel(INT_SOURCE_E eIntNo, UINT32 uIntLevel)
{
   UINT32 _mRegAddr, _mRegValue;

   if ((eIntNo > WB_MAX_INT_SOURCE) || (eIntNo < WB_MIN_INT_SOURCE))
      return Fail;

   _mRegAddr = WB_AICRegAddr[eIntNo];
   _mRegValue = inpw(_mRegAddr) & 0xFFFFFFF8;
   outpw(_mRegAddr, (_mRegValue | uIntLevel));

   return Successful;
}

INT32 sysSetInterruptType(INT_SOURCE_E eIntNo, UINT32 uIntSourceType)
{
   UINT32 _mRegAddr, _mRegValue;

   if ((eIntNo > WB_MAX_INT_SOURCE) || (eIntNo < WB_MIN_INT_SOURCE))
      return Fail;

   _mRegAddr = WB_AICRegAddr[eIntNo];
   _mRegValue = inpw(_mRegAddr) & 0xFFFFFF3F;
   outpw(_mRegAddr, (_mRegValue | uIntSourceType));

   return Successful;
}

INT32 sysSetLocalInterrupt(INT32 nIntState)
{
   INT32 temp;

   switch (nIntState)
   {
      case ENABLE_IRQ:
      case ENABLE_FIQ:
      case ENABLE_FIQ_IRQ:
           __asm
           {
               MRS    temp, CPSR
               AND    temp, temp, nIntState
               MSR    CPSR_c, temp
           }
           break;

      case DISABLE_IRQ:
      case DISABLE_FIQ:
      case DISABLE_FIQ_IRQ:
           __asm
           {
               MRS    temp, CPSR
               ORR    temp, temp, nIntState
               MSR    CPSR_c, temp
           }
           break;

      default:
           ;
   }
   return Successful;
}

INT32 sysSetAIC2SWMode()
{
	_sys_bIsHWMode = FALSE;
	return Successful;
}


UINT32	sysGetInterruptEnableStatus(VOID)
{
    return (inpw(REG_AIC_IMR));
}


BOOL sysGetIBitState()
{
	INT32 temp;

	__asm
	{
		MRS	temp, CPSR
	}

	if (temp & 0x80)
		return FALSE;
	else
		return TRUE;
}


